package com.gcalsync.component;

import java.util.Vector;
import javax.microedition.lcdui.*;
import com.gcalsync.gcal.GCalEvent;
import com.gcalsync.gcal.GCalClient;
import com.gcalsync.store.Store;

public class PreviewComponent extends MVCComponent 
{
	static final Command CMD_CANCEL = new Command("Cancel", Command.CANCEL, 4);
	static final Command CMD_COMMIT = new Command("Commit", "Commit selected events", Command.ITEM, 2);
	static final Command CMD_SELECT_DL = new Command("Select DLs", "Select all download events", Command.ITEM, 1);
	static final Command CMD_SELECT_UL = new Command("Select ULs", "Select all upload events", Command.ITEM, 1);
	static final Command CMD_UNSELECT = new Command("Unselect", "Unselect all events", Command.ITEM, 1);

    Form form;
	GCalClient gCalClient;
	ChoiceGroup[] uploadChoices;
	ChoiceGroup[] downloadChoices;
	GCalEvent[] uploads;
	GCalEvent[] downloads;

	/**
    * Constructor
	*/
	public PreviewComponent(GCalClient gcal)
	{
		gCalClient = gcal;
	}

	/**
    * Gets the form for this component
    *
    * @returns <code>form</code>
	*/
    public Displayable getDisplayable() {return this.form;}

	/**
    * Updates the view after it is created
	*/
	protected void updateView() {}

	/**
    * Initializes the model before creating the view
	*/
    protected void initModel() {}

	/**
    * Creates the view
	*/
    protected void createView() 
	{
        this.form = new Form("Preview");

		//add upload choices to form
        if (uploads != null) addEvents(true);

		//add download choices to form
		if (downloads != null) addEvents(false);

		//add commit command only if choices exist
		if ((this.downloads != null && this.downloads.length > 0) 
			|| (this.uploads != null && this.uploads.length > 0))
		{
			this.form.addCommand(CMD_COMMIT);
			this.form.addCommand(CMD_UNSELECT);
		}

		//add commands to form
		if (this.downloads != null && this.downloads.length > 0)
			this.form.addCommand(CMD_SELECT_DL);

		if (this.uploads != null && this.uploads.length > 0)
			this.form.addCommand(CMD_SELECT_UL);

        this.form.addCommand(CMD_CANCEL);
        this.form.setCommandListener(this);
    }

	/**
    * Processes menu commands
    *
    * @param c command to execute
    * @param displayable <code>Displayable</code> from which
    *                    <code>command</code> originates
	*/
    public void commandAction(Command c, Displayable displayable) 
	{
		CommitComponent cmt;
		Alert alert;

		if (c == CMD_SELECT_DL)
		{
			selectAll(this.downloadChoices, true);
		}
		else if (c == CMD_SELECT_UL)
		{
			selectAll(this.uploadChoices, true);
		}
		else if (c == CMD_UNSELECT)
		{
			selectAll(this.uploadChoices, false);
			selectAll(this.downloadChoices, false);
		}
		else if (c == CMD_COMMIT) 
		{
			if (isSelected(this.uploadChoices)
				|| isSelected(this.downloadChoices))
			{
				//remove events that are not selected from the uploads/downloads
				//and pass them to the CommitComponent
				cmt = new CommitComponent(gCalClient);
				cmt.setEvents(getSelectedEvents(this.uploadChoices, this.uploads), 
							  getSelectedEvents(this.downloadChoices, this.downloads));

				//start CommitComponent on new thread to prevent phone lockup
				cmt.handle();
			}
			else
			{
				//show error for 2 seconds
				alert = new Alert("Error", "No events selected", null, AlertType.ERROR);
				alert.setTimeout(1500);
				this.display.setCurrent(alert, this.display.getCurrent());
			}
        }
		else if (c == CMD_CANCEL) 
		{
			Components.login.showScreen();
		}
    }

	/**
    * Sets the upload and download events to be previewed
    *
    * @param uploads events to be uploaded
    * @param downloads events to be downloaded
	*/
	public void setEvents(GCalEvent[] uploads, GCalEvent[] downloads)
	{
		if (uploads != null)
		{
			this.uploads = new GCalEvent[uploads.length];
			System.arraycopy(uploads, 0, this.uploads, 0, uploads.length);
		}

		if (downloads != null)
		{
			this.downloads = new GCalEvent[downloads.length];
			System.arraycopy(downloads, 0, this.downloads, 0, downloads.length);
		}
	}

	/*
    * Adds events to the current form
    *
    * @param upload if true, adds the upload events; otherwise, adds the download events
	*/
	void addEvents(boolean upload)
	{
		ChoiceGroup[] choices;
		GCalEvent[] events;
		StringItem title;
		long timeOffset;

		if (upload)
		{
			events = this.uploads;
			this.uploadChoices = new ChoiceGroup[events.length];
			choices = this.uploadChoices;
			title = new StringItem("Uploads", null);
			timeOffset = Store.getOptions().uploadTimeZoneOffset;
		}
		else
		{
			events = this.downloads;
			this.downloadChoices = new ChoiceGroup[events.length];
			choices = this.downloadChoices;
			title = new StringItem("Downloads", null);
			timeOffset = Store.getOptions().downloadTimeZoneOffset;
		}

		//add group
		title.setFont(Font.getFont(Font.FACE_PROPORTIONAL, 
								   Font.STYLE_BOLD,
								   Font.SIZE_SMALL));
		this.form.append(title);

		if (events.length == 0)
		{
			this.form.append(new StringItem("", "*None*"));
		}
		else
		{
			for (int i = 0; i < events.length; i++)
			{
				choices[i] = new ChoiceGroup(null, ChoiceGroup.MULTIPLE, new String[]{events[i].toString(timeOffset)}, null);
				this.form.append(choices[i]);
				this.form.append(new Spacer(getDisplayable().getWidth(), 8));
			}
		}
	}

	/**
    * Gets all events that correspond to the selected elements in
    * the specified <code>ChoiceGroup</code> array
    *
    * @param choices ChoiceGroups to search
    * @param events list of events
	*/
	GCalEvent[] getSelectedEvents(ChoiceGroup[] choices, GCalEvent[] events)
	{
		GCalEvent[] rval = null;

		if (choices != null && choices.length > 0 && events != null)
		{
			Vector eventVect = new Vector(choices.length);
			
			//add selected events to the event vector
			for (int i=0; i<choices.length; i++)
			{
				if (choices[i].isSelected(0)) 
					eventVect.addElement(events[i]);
			}

			//replace <events> with events from the event vector
			if (eventVect.size() > 0)
			{
				rval = new GCalEvent[eventVect.size()];
				eventVect.copyInto(rval);
			}
		}

		return rval;
	}

	void selectAll(ChoiceGroup[] choices, boolean selected)
	{
		for (int i=0; i<choices.length; i++)
			choices[i].setSelectedIndex(0, selected);
	}

	/**
    * Determines if the specified ChoicGroup array contains any
    * elements that are selected
    *
    * @param choices ChoiceGroups to search
	*/
	boolean isSelected(ChoiceGroup[] choices)
	{
		boolean rval = false;

		for (int i=0; i<choices.length; i++)
		{
			if (choices[i].isSelected(0))
			{
				rval = true;
				break;
			}
		}

		return rval;
	}
}

